#include "output_wrapper.h"
#include "wlroots-full.hpp"
#include "server.h"

OutputWrapper::OutputWrapper(struct wlr_output* output, Server* server)
    : m_server(server)
    , m_wlr_output(output)
{
#if 0
    m_wlr_output->data = this;
    wlr_output_effective_resolution(m_wlr_output,
                                    &m_usable_area.width, &m_usable_area.height);
#endif

    m_destroy.connect(&m_wlr_output->events.destroy, [=](void* data) {
        m_frame.disconnect();
        m_request_state.disconnect();
        sig_destroy.emit();
        m_destroy.disconnect();
        delete this;
    });

    m_frame.connect(&m_wlr_output->events.frame, [=](void* data) {
        output_frame(data);
    });

    m_request_state.connect(&m_wlr_output->events.request_state, [=](void* data) {
        output_request_state(data);
    });

    // wl_list_init(&output->regions);

#if 0
    /*
     * Create layer-trees (background, bottom, top and overlay) and
     * a layer-popup-tree.
     */
    // for (size_t i = 0; i < ARRAY_SIZE(layer_tree); i++) {
    for(auto& layer: m_layer_tree) {
        layer = wlr_scene_tree_create(&m_server->m_scene->tree);
        // node_descriptor_create(&layer->node, LAB_NODE_DESC_TREE, NULL);
    }

    m_layer_popup_tree = wlr_scene_tree_create(&m_server->m_scene->tree);
    // node_descriptor_create(&output->layer_popup_tree->node,
                           // LAB_NODE_DESC_TREE, NULL);
    m_osd_tree = wlr_scene_tree_create(&m_server->m_scene->tree);
    // node_descriptor_create(&output->osd_tree->node,
                           // LAB_NODE_DESC_TREE, NULL);
    m_session_lock_tree = wlr_scene_tree_create(&m_server->m_scene->tree);
    // node_descriptor_create(&output->session_lock_tree->node,
                           // LAB_NODE_DESC_TREE, NULL);

    /*
     * Set the z-positions to achieve the following order (from top to
     * bottom):
     *	- session lock layer
     *	- layer-shell popups
     *	- overlay layer
     *	- top layer
     *	- views
     *	- bottom layer
     *	- background layer
     */
    wlr_scene_node_lower_to_bottom(&m_layer_tree[1]->node);
    wlr_scene_node_lower_to_bottom(&m_layer_tree[0]->node);
    wlr_scene_node_raise_to_top(&m_layer_tree[2]->node);
    wlr_scene_node_raise_to_top(&m_layer_tree[3]->node);
    wlr_scene_node_raise_to_top(&m_layer_popup_tree->node);
    wlr_scene_node_raise_to_top(&m_session_lock_tree->node);
#endif
    /*
     * Wait until wlr_output_layout_add_auto() returns before
     * calling do_output_layout_change(); this ensures that the
     * wlr_output_cursor is created for the new output.
     */
    m_server->m_pending_output_layout_change++;

    add_to_layout();

    // /* Create regions from config */
    // regions_reconfigure_output(output);

    // if (server->session_lock) {
        // session_lock_output_create(server->session_lock, output);
    // }

    m_server->m_pending_output_layout_change--;
    do_output_layout_change();
    // seat_output_layout_changed(&output->server->seat);
}

OutputWrapper::~OutputWrapper()
{
    output_destroy();
}

void OutputWrapper::output_destroy()
{
#if 0
    struct output *output = wl_container_of(listener, output, destroy);
    regions_evacuate_output(output);
    regions_destroy(&output->server->seat, &output->regions);
    wl_list_remove(&output->link);
    wl_list_remove(&output->frame.link);
    wl_list_remove(&output->destroy.link);
    wl_list_remove(&output->request_state.link);
    seat_output_layout_changed(&output->server->seat);

    for (size_t i = 0; i < ARRAY_SIZE(output->layer_tree); i++) {
        wlr_scene_node_destroy(&output->layer_tree[i]->node);
    }
    wlr_scene_node_destroy(&output->layer_popup_tree->node);
    wlr_scene_node_destroy(&output->osd_tree->node);
    wlr_scene_node_destroy(&output->session_lock_tree->node);
    if (output->workspace_osd) {
        wlr_scene_node_destroy(&output->workspace_osd->node);
        output->workspace_osd = NULL;
    }

    struct view *view;
    struct server *server = output->server;
    wl_list_for_each(view, &server->views, link) {
        if (view->output == output) {
            view_on_output_destroy(view);
        }
    }

    /*
     * Ensure that we don't accidentally try to dereference
     * the output pointer in some output event handler like
     * set_gamma.
     */
    output->wlr_output->data = NULL;

    /*
     * output->scene_output (if still around at this point) is
     * destroyed automatically when the wlr_output is destroyed
     */
    free(output);
#endif
}

void OutputWrapper::output_frame(void *data)
{
    /*
     * This function is called every time an output is ready to display a
     * frame - which is typically at 60 Hz.
     */
    if (!is_usable()) {
        return;
    }

#if 0
    if (m_gamma_lut_changed) {
        struct wlr_output_state pending;
        wlr_output_state_init(&pending);
        if (!wlr_scene_output_build_state(m_scene_output, &pending, NULL)) {
            return;
        }
        m_gamma_lut_changed = false;
        struct wlr_gamma_control_v1 *gamma_control =
            wlr_gamma_control_manager_v1_get_control(
                m_server->m_gamma_control_manager_v1, m_wlr_output);
        if (!wlr_gamma_control_v1_apply(gamma_control, &pending)) {
            wlr_output_state_finish(&pending);
            return;
        }

        if (!wlr_output_commit_state(m_wlr_output, &pending)) {
            wlr_gamma_control_v1_send_failed_and_destroy(gamma_control);
            wlr_output_state_finish(&pending);
            return;
        }

        wlr_damage_ring_rotate(&m_scene_output->damage_ring);
        wlr_output_state_finish(&pending);
        return;
    }
#endif
#if 0
    m_wlr_output->pending.tearing_page_flip = get_tearing_preference();
    // lab_wlr_scene_output_commit(m_scene_output);
    wlr_scene_output_commit(m_scene_output, NULL);

    struct timespec now = { 0 };
    clock_gettime(CLOCK_MONOTONIC, &now);
    wlr_scene_output_send_frame_done(m_scene_output, &now);
#else
    struct wlr_scene_output *scene_output = wlr_scene_get_scene_output(
        m_server->m_scene, m_wlr_output);

    /* Render the scene if needed and commit the output */
    wlr_scene_output_commit(scene_output, NULL);

    struct timespec now;
    clock_gettime(CLOCK_MONOTONIC, &now);
    wlr_scene_output_send_frame_done(scene_output, &now);
#endif
}

void OutputWrapper::output_request_state(void *data)
{
    const struct wlr_output_event_request_state *event = (const struct wlr_output_event_request_state*)data;

    if (!wlr_output_commit_state(m_wlr_output, event->state)) {
        wlr_log(WLR_ERROR, "Backend requested a new state that could not be applied");
    }
}

void OutputWrapper::add_to_layout()
{
    struct wlr_output_layout_output *layout_output =
        wlr_output_layout_add_auto(m_server->m_output_layout, m_wlr_output);
    if (!layout_output) {
        wlr_log(WLR_ERROR, "unable to add output to layout");
        return;
    }

    if (!m_scene_output) {
        m_scene_output =
            wlr_scene_output_create(m_server->m_scene, m_wlr_output);
        if (!m_scene_output) {
            wlr_log(WLR_ERROR, "unable to create scene output");
            return;
        }
        /*
         * Note: wlr_scene_output_layout_add_output() is not
         * safe to call twice, so we call it only when initially
         * creating the scene_output.
         */
        wlr_scene_output_layout_add_output(m_server->m_scene_layout,
                                           layout_output, m_scene_output);
    }

}

void OutputWrapper::do_output_layout_change()
{
    // if (!m_server->m_pending_output_layout_change) {
    //     struct wlr_output_configuration_v1 *config =
    //         create_output_config(server);
    //     if (config) {
    //         wlr_output_manager_v1_set_configuration(
    //             m_server->m_output_manager, config);
    //     } else {
    //         wlr_log(WLR_ERROR,
    //                 "wlr_output_manager_v1_set_configuration()");
    //     }
    //     output_update_for_layout_change(server);
    // }
}

bool OutputWrapper::is_usable()
{
    /* output_is_usable(NULL) is safe and returns false */
    return m_wlr_output->enabled && !m_leased;
}

bool OutputWrapper::get_tearing_preference()
{
#if 0
    struct server *server = output->server;

    /* Never allow tearing when disabled */
    if (!rc.allow_tearing) {
        return false;
    }

    /* Tearing is only allowed for the output with the active view */
    if (!server->active_view || server->active_view->output != output) {
        return false;
    }

    /* If the active view requests tearing, or it is toggled on with action, allow it */
    return server->active_view->tearing_hint;
#else
    return false;
#endif
}
